/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

/*!
  \file fbManager.cxx
  \author Cory Dixon
*/

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>

#include <stdlib.h>
#include <stdio.h>

#include "fbManager.h"
#include "vcConfig.h"

extern GuiStruct guiStr;

/*static GtkActionEntry fb_entries[] = 
{ct
	//------------- Layers Menu --------------------//
	{ "CoAName", GTK_STOCK_EXECUTE,     // name, stock id
		"_CoA Name", NULL,      // label, accelerator
		"Name of CoA Boundary"   // tooltip
	},
};*/

/*<-End Global Variables-->*/

FBManager::FBManager() : LayerManager()
{
	lineWidth  = 2.0;
	num_boundaries  = 0;
}

FBManager::~FBManager()
{
}

void FBManager::init(GPSMap *map)
{
	LayerManager::init(map);
	layerG = GOO_CANVAS_ITEM(gps_map->getLayerG(GPSMap::FB_LAYER));
	//cout << "layerG= " << layerG << " map=" << map << endl;

	// Create new GtkActionGroup and add entries 
	//action_group = gtk_action_group_new ("FBActions");
	//gtk_action_group_add_actions (action_group, fb_entries, G_N_ELEMENTS(fb_entries), this);

	// insert action group into menu_manager
	//menu_manager = gps_map->get_MenuManager();
	//gtk_ui_manager_insert_action_group (menu_manager, action_group, GPSMap::FB_LAYER);
}

// The flight boundarys don't ned to be udated by a timeout function as 
// they are only changed on user events or downloaded boundarys from vehicles,
// which will both cause their appropriate update methods
void FBManager::updateMap()
{
	//cout << "FBManager::updateMap()" << endl;
	return;
}

void FBManager::translate_from_zoomChange(double zoom_factor)
{
	//cout << "FBManager::translate_from_zoomChange - zoom_factor=" << zoom_factor << endl;
	double x,y;
	
	//--  loop through all clients flight plans --//
	for( int fbInd = 0; fbInd < MAX_BOUNDARIES+1; fbInd++){
		//--  loop through and convert all lat/lon points to image within flightplans --//

		for( int pInd = 0; pInd < fb[fbInd].numPoints; pInd++ ) {
			// get lat/lon coordinates
			x = fb[fbInd].b_point[pInd].lon;
			y = fb[fbInd].b_point[pInd].lat;

			// transform into window
			gps_map->geoToLayer(GPSMap::FP_LAYER, x, y);

			// save to array
			fb[fbInd].imgPoints[pInd].x = x;
			fb[fbInd].imgPoints[pInd].y = y;

			GooCanvasPoints *g_points = goo_canvas_points_new(fb[fbInd].numPoints);
			double points[ fb[fbInd].numPoints * 2];
			for( int i=0; i < fb[fbInd].numPoints; i++) {
				points[i*2+0] = fb[fbInd].imgPoints[i].x;
				points[i*2+1] = fb[fbInd].imgPoints[i].y;
				g_points->coords[i*2+0] = fb[fbInd].imgPoints[i].x;
				g_points->coords[i*2+1] = fb[fbInd].imgPoints[i].y;
			}
			g_object_set (G_OBJECT (fb[fbInd].icon), "points", g_points, NULL);
			goo_canvas_points_unref(g_points);
		}

		if(fb[fbInd].text != NULL) {
			goo_canvas_item_set_simple_transform( fb[fbInd].text, x, y, 1.0, 0.0);
		}

	}
}

void FBManager::create_canvas_flightboundary(int boundaryInd, uint32_t fill_color)
{
	//cout << "FBManager::create_canvas_flightboundary - clientInd=" << clientInd << " fbG=" << layerG << endl;

	double x,y;

	//--  loop through and convert all lat/lon points to image --//
	for( int i = 0; i < fb[boundaryInd].numPoints; i++ ) {
		// get lat/lon coordinates
		x = fb[boundaryInd].b_point[i].lon;
		y = fb[boundaryInd].b_point[i].lat;

		// transform into window
		gps_map->geoToLayer(GPSMap::FB_LAYER, x, y);

		// save to array
		fb[boundaryInd].imgPoints[i].x = x;
		fb[boundaryInd].imgPoints[i].y = y;
	}

	uint32_t stroke_color = 0x000000FF;
	//uint32_t fill_color   = 0x0000FF22;

	// Create group to hold boundary 
	if( fb[boundaryInd].item == NULL) 
	{
		fb[boundaryInd].item = goo_canvas_group_new (GOO_CANVAS_ITEM(layerG), NULL);

		gps_map->addLayer( fb[boundaryInd].name, fb[boundaryInd].item, (GooCanvasItem*)GPSMap::FB_LAYER);
		gps_map->setLayerListShown( fb[boundaryInd].item, true);

		setup_item(GOO_CANVAS_ITEM(fb[boundaryInd].item));

		// make sure parent manager layer is shown
		gps_map->toggleLayerShown(GPSMap::FB_LAYER, true);
	}

	/*x = fb[boundaryInd].b_point[0].lon;
	y = fb[boundaryInd].b_point[0].lat;

	// transform into window
	gps_map->geoToLayer(GPSMap::FP_LAYER, x, y);

	fb[boundaryInd].text =  goo_canvas_text_new (fb[boundaryInd].item, fb[boundaryInd].text_str, 
			x+8, y+8, -1, GTK_ANCHOR_NW,
			"font", "Sans 8px",
			"fill-color", "black",
			NULL);

	sprintf( fb[boundaryInd].text_str, "%s", fb[boundaryInd].name.c_str());
	if( fb[boundaryInd].text ) {
		g_object_set(G_OBJECT(fb[boundaryInd].text), "text", fb[boundaryInd].text_str, NULL);
	}*/

	GooCanvasPoints *g_points = goo_canvas_points_new(fb[boundaryInd].numPoints);
	double points[ fb[boundaryInd].numPoints * 2];
	for( int i=0; i < fb[boundaryInd].numPoints; i++) {
		points[i*2+0] = fb[boundaryInd].imgPoints[i].x;
		points[i*2+1] = fb[boundaryInd].imgPoints[i].y;
		g_points->coords[i*2+0] = fb[boundaryInd].imgPoints[i].x;
		g_points->coords[i*2+1] = fb[boundaryInd].imgPoints[i].y;
	}

	fb[boundaryInd].icon = goo_canvas_polyline_new (fb[boundaryInd].item, TRUE, 2,
			points[0], points[1],
			points[2], points[3],
			"line-width", lineWidth,
			"stroke-color-rgba", stroke_color,
			"fill-color-rgba", fill_color,
			NULL);

	g_object_set (G_OBJECT (fb[boundaryInd].icon), "points", g_points, NULL);

	goo_canvas_points_unref(g_points);
}

void FBManager::create_canvas_line_flightboundary(int boundaryInd)
{

	double x,y;

	//--  loop through and convert all lat/lon points to image --//
	for( int i = 0; i < fb[boundaryInd].numPoints; i++ ) {
		// get lat/lon coordinates
		x = fb[boundaryInd].b_point[i].lon;
		y = fb[boundaryInd].b_point[i].lat;

		// transform into window
		gps_map->geoToLayer(GPSMap::FB_LAYER, x, y);

		// save to array
		fb[boundaryInd].imgPoints[i].x = x;
		fb[boundaryInd].imgPoints[i].y = y;
	}

	uint32_t stroke_color = 0x000000FF;

	// Create group to hold boundary 
	if( fb[boundaryInd].item == NULL) 
	{
		fb[boundaryInd].item = goo_canvas_group_new (GOO_CANVAS_ITEM(layerG), NULL);

		gps_map->addLayer( fb[boundaryInd].name, fb[boundaryInd].item, (GooCanvasItem*)GPSMap::FB_LAYER);
		gps_map->setLayerListShown( fb[boundaryInd].item, true);

		// make sure parent manager layer is shown
		gps_map->toggleLayerShown(GPSMap::FB_LAYER, true);
	}

	GooCanvasPoints *g_points = goo_canvas_points_new(fb[boundaryInd].numPoints);
	double points[ fb[boundaryInd].numPoints * 2];
	for( int i=0; i < fb[boundaryInd].numPoints; i++) {
		points[i*2+0] = fb[boundaryInd].imgPoints[i].x;
		points[i*2+1] = fb[boundaryInd].imgPoints[i].y;
		g_points->coords[i*2+0] = fb[boundaryInd].imgPoints[i].x;
		g_points->coords[i*2+1] = fb[boundaryInd].imgPoints[i].y;
	}

	fb[boundaryInd].icon = goo_canvas_polyline_new (fb[boundaryInd].item, FALSE, 2,
			points[0], points[1],
			points[2], points[3],
			"stroke-color-rgba", stroke_color,
			"line-width", lineWidth,
			"end-arrow", FALSE,
			NULL);

	g_object_set (G_OBJECT (fb[boundaryInd].icon), "points", g_points, NULL);
	goo_canvas_points_unref(g_points);
}

void FBManager::setup_item(GooCanvasItem *item)
{  
	g_signal_connect (G_OBJECT (item), "button-press-event", 
			G_CALLBACK (FBManager::button_press_event_cb), this);
	//g_signal_connect (G_OBJECT (item), "button-release-event", 
			//G_CALLBACK (FBManager::button_release_event_cb), this);
	//g_signal_connect (G_OBJECT (item), "motion-notify-event", 
			//G_CALLBACK (FBManager::motion_notify_event_cb), this);
}     

bool FBManager::button_press_event_cb (GooCanvasItem *item, GooCanvasItem *target_item, GdkEventButton *event, FBManager *me)
{
	//cout << "FBManager::button_press_event_cb: " 
		//<< " me=" << me
		//<< " event->type=" << event->type 
		//<<"  event->x=" <<  event->x
		//<< " event->y=" <<  event->y << endl;

	if( me == NULL )
		return FALSE;

	int boundary = -1;
	for( int nB=0; nB < MAX_BOUNDARIES+1; nB ++) {
		if(item == me->fb[nB].item) {
			boundary = nB;
		}
	}

	if(boundary < 0) return FALSE;

	switch(event->button) {
		case LEFT_MOUSE:
			//return TRUE;
			break;

		case RIGHT_MOUSE:
			//return TRUE;
			break;

		case MIDDLE_MOUSE:
			cout << me->fb[boundary].name << endl;
			//me->create_control_window(event, me->fb[boundary].name);
			break;

		default:
			printf("Unknown mouse button value\n");
			break;
	} 

	// Returning FALSE propagates the event to parent items;
	// returning TRUE ends event propagation. 
	return FALSE;
}

bool FBManager::button_release_event_cb(GooCanvasItem *item, GooCanvasItem *target_item, GdkEventButton *event, FBManager *me)
{
	//cout << "FBManager::button_release_event_cb" << endl;
	if( me == NULL )
		return FALSE;

	// Returning FALSE propagates the event to parent items;
	// returning TRUE ends event propagation. 
	return FALSE;
}

bool FBManager::motion_notify_event_cb(GooCanvasItem *item, GooCanvasItem *target_item, GdkEventMotion *event, FBManager *me)
{
	//cout << "FBManager::motion_notify_event_cb" << endl;
	if( me == NULL )
		return FALSE;

	// Returning FALSE propagates the event to parent items;
	// returning TRUE ends event propagation. 
	return FALSE;
}

int FBManager::create_control_window( GdkEventButton *event, String name)
{
	// Get the popup menu
	gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
	GtkToggleAction* action = GTK_TOGGLE_ACTION(gtk_action_group_get_action(action_group, "CoAName"));
	if(action) gtk_toggle_action_set_active(action,0);

	return TRUE;
}

#ifdef USING_KML
bool FBManager::AddChild(const kmldom::ElementPtr& parent, const kmldom::ElementPtr& child) {
	kmldom::DocumentPtr document;
	kmldom::FolderPtr folder;
	switch(child->Type()) {
		case kmldom::Type_Document:
			document = kmldom::AsDocument(child);
			cout << (document->get_name()) << endl;
			break;
		case kmldom::Type_Folder:
			folder = kmldom::AsFolder(child);
			if(!strcmp(folder->get_name().c_str(),"CoA Boundaries")) {
				kmldom::PlacemarkPtr placemark;
				for(size_t i = 0; i< folder->get_feature_array_size(); i++) {
					if((folder->get_feature_array_at(i))->IsA(kmldom::Type_Placemark)) {
						placemark=AsPlacemark(folder->get_feature_array_at(i));
						fb[num_boundaries].name = placemark->get_name().c_str();
						if((placemark->get_geometry())->IsA(kmldom::Type_Polygon)) {
							kmldom::PolygonPtr polygon = AsPolygon(placemark->get_geometry());
							kmldom::OuterBoundaryIsPtr outer_boundary_is = AsOuterBoundaryIs(polygon->get_outerboundaryis());
							if((outer_boundary_is->get_linearring())->IsA(kmldom::Type_LinearRing)) {
								kmldom::LinearRingPtr linear_ring = AsLinearRing(outer_boundary_is->get_linearring());
								kmldom::CoordinatesPtr coordinates = AsCoordinates(linear_ring->get_coordinates());
								fb[num_boundaries].numPoints = 0;
								for(size_t j = 0; j < (coordinates->get_coordinates_array_size()-1); j++) {
									kmlbase::Vec3 vector = coordinates->get_coordinates_array_at(j);
									fb[num_boundaries].b_point[ fb[num_boundaries].numPoints ].lat=vector.get_latitude();
									fb[num_boundaries].b_point[ fb[num_boundaries].numPoints ].lon=vector.get_longitude();
									fb[num_boundaries].numPoints++;
									//cout << "(" << vector.get_longitude() <<  "," << vector.get_latitude() << "," << vector.get_altitude() << ")" << endl;

								}
								if(num_boundaries < MAX_BOUNDARIES) {
									create_canvas_flightboundary(num_boundaries);
									num_boundaries++;
								}
							}
						}
					}
				}
			}

			if(!strcmp(folder->get_name().c_str(),"Test Bed Boundaries")) {
				kmldom::PlacemarkPtr placemark;
				for(size_t i = 0; i< folder->get_feature_array_size(); i++) {
					if((folder->get_feature_array_at(i))->IsA(kmldom::Type_Placemark)) {
						placemark=AsPlacemark(folder->get_feature_array_at(i));
						fb[num_boundaries].name = placemark->get_name().c_str();
						if((placemark->get_geometry())->IsA(kmldom::Type_LineString)) {
							kmldom::LineStringPtr line_string = AsLineString(placemark->get_geometry());
							kmldom::CoordinatesPtr coordinates = AsCoordinates(line_string->get_coordinates());
							fb[num_boundaries].numPoints = 0;
							for(size_t j = 0; j < (coordinates->get_coordinates_array_size()); j++) {
								kmlbase::Vec3 vector = coordinates->get_coordinates_array_at(j);

#define GRID_SCALE 0.45
								//float scaled_y = vector.get_latitude()*GRID_SCALE;
								//float scaled_x = vector.get_longitude()*GRID_SCALE;

								//float lat = M_TO_LAT(scaled_y);
								//float lon = M_TO_LON(scaled_x,lat);
								
								//NOT USING ACTUAL UNITS
								float lat = vector.get_latitude()*GRID_SCALE;
								float lon = vector.get_longitude()*GRID_SCALE;

								fb[num_boundaries].b_point[ fb[num_boundaries].numPoints ].lat = lat;
								fb[num_boundaries].b_point[ fb[num_boundaries].numPoints ].lon = lon;
								fb[num_boundaries].numPoints++;
							}
							if(num_boundaries < MAX_BOUNDARIES) {
								create_canvas_line_flightboundary(num_boundaries);
								num_boundaries++;
							}
						}
					}
				}
			}
			return false;
			break;
		default:
			break;
	}
	return true;
}
#endif

bool FBManager::read_from_file(String file)
{
	cout << "Loading boundary file  : ";

	ifstream inputFile(file.c_str(), ios::in);
	if( !inputFile )	{
		perror("Could not open file");
		return false;
	} else {

		int length;
		char * buffer;

		inputFile.seekg (0, ios::end);
		length = inputFile.tellg();
		inputFile.seekg (0, ios::beg);
		
		// allocate memory:
		buffer = new char [length];

		// read data as a block:
		inputFile.read (buffer,length);
		inputFile.close();

		std::string errors;

#ifdef USING_KML
		kmldom::Parser parser;
		parser.AddObserver(this);
		kmldom::ElementPtr root = parser.Parse(buffer,&errors);
		delete buffer;
		if (!root) {
			cout << errors << endl;
			//return false;
		}
#endif
	}
	return true;
}

int FBManager::clear_fb_cb(GtkAction *action, FBManager *me)
{
	if( me == NULL )
		return FALSE;

	me->clearFB();

	// Returning FALSE propagates the event to parent items;
	// returning TRUE ends event propagation. 
	return TRUE;
}

void FBManager::clearFB()
{

	//--  loop through all boundaries --//
	for( int nB=0; nB < MAX_BOUNDARIES+1; nB ++) {

		//--  loop through and convert all lat/lon points to image --//
		for( int i = 0; i < fb[nB].numPoints; i++ ) {
			// clear the point
			fb[nB].b_point[i].clear();
		}
		fb[nB].numPoints = 0;

		// Delete boundary group 
		if( fb[nB].item) 
		{
			gps_map->delLayer( fb[nB].item);
			goo_canvas_item_remove(GOO_CANVAS_ITEM(fb[nB].item));
			fb[nB].item = NULL;
		}
		fb[nB].icon = NULL;
		fb[nB].text = NULL;
	}
	// clear number of stored boundaries
	num_boundaries = 0;

	// make sure parent manager layer is shown
	gps_map->toggleLayerShown(GPSMap::FB_LAYER, false);
}
